/**
 * @fileoverview Dx.Base
 * Base Class of all Dx classes
 */

///////////////////////////////////////////////////////////////////////////////
// Base Class
///////////////////////////////////////////////////////////////////////////////

/**
 * @const Base Class constructor
 */
Dx.Base = Dx.createConstructor();

Dx.assign(Dx.Base.prototype,

   /** @lends Dx.Base.prototype */ ({

   /**
    * list of static methods to apply when extending this class
    */
   statics: ['extend', 'override'],

   /**
    * constructor
    */
   construct: function() {},

   /**
    * call super class
    */
   $super: function(args) {

      var caller = arguments.callee.caller;

      var id = caller && caller._super_id;

      var returned = void(0);

      var list = Dx.Base.methods;

      if (id && id in list) {

         returned = list[id].apply(this, args || []);

         caller = null;

      }

      return returned;

   },

   $overridden: function(args) {

      var caller = arguments.callee.caller;

      var id = caller && caller._override_id;

      var returned = void(0);

      var list = Dx.Base.methods;

      if (id && id in list) {

         returned = list[id].apply(this, args || []);

         caller = null;

      }

      return returned;

   }

}));

///////////////////////////////////////////////////////////////////////////////
// Base Class Static Properties
///////////////////////////////////////////////////////////////////////////////

Dx.assign(Dx.Base,
/** @lends Dx */ {

/**
 * Methods id generation counter
 */
   methodsGen: 0,

/**
 * Methods list by id
 */
   methods: {},

/**
 * get method id from function
 * @param {String} name
 * @param {Object} Prototype
 * @return {String} registered method id
 */
   registerMethod: function(name, Prototype) {

      var old = name in Prototype && Prototype[name];

      var id = void(0);

      var D = Dx;

      var Base = D.Base;

      if (D.is('Function', old)) {

         id = old.method_id;

         // register if not yet registered
         if (!id) {

            id = 'method-id' + (++Base.methodsGen);

            old.method_id = id;

            Base.methods[id] = old;

         }

      }

      return id;

   },


/**
 * Creates Dx Class with properties defined in properties argument
 * @this current Superclass constructor
 * @param {Object} properties properties defined for the newly created Class
 * @return {Function} Dx Class constructor
 */
   extend: function(properties) {

      var D = Dx;

      var Base = D.Base;

      var Class = D.createConstructor();

      var SuperClass = this;

      var Proto = D.createObject(SuperClass);

      var statics, l, name;

      if (D.isObject(properties)) {

         if (properties.hasOwnProperty('constructor')) {

            properties.construct = properties.constructor;

            delete properties.constructor;

         }

         D.each(properties, Base._callback_extendProperty, Proto);

      }

      Class.prototype = Proto;

      Proto.constructor = Class;

      Proto.Superclass = SuperClass.prototype;

      // apply new class static methods
      for (statics = Proto.statics, l = statics.length; l--;) {

         name = statics[l];

         if (name in SuperClass) {

            Class[name] = SuperClass[name];

         }

      }

      return Class;


   },

/**
 * callback when extending property from Class.extend();
 * @this current Superclass constructor
 * @param {String} name property name iterated
 * @param {*} value property value iterated
 * @param {Object} properties the property object
 */
   _callback_extendProperty: function(name, value, properties) {

      var D = Dx;

      var method_id;

      if (properties.hasOwnProperty(name)) {

         if (D.is('Function', value)) {

            method_id = D.Base.registerMethod(name, this);

            if (method_id) {

               value._super_id = method_id;

            }

         }

         this[name] = value;

      }

   },

/**
 * overrides properties and methods from the current Class
 * @this current Superclass constructor
 * @param {Object} properties properties defined for the newly created Class
 * @return {Function} Dx Class constructor
 */
   override: function(properties) {

      var D = Dx;

      var Class = this;

      if (D.isObject(properties)) {

         if (properties.hasOwnProperty('constructor')) {

            properties.construct = properties.constructor;

            delete properties.constructor;

         }

         D.each(properties, D.Base._callback_overrideProperty, Class.prototype);

      }

      return Class;

   },

/**
 * callback when overriding property from Class.override();
 * @this current Superclass constructor
 * @param {String} name property name iterated
 * @param {*} value property value iterated
 * @param {Object} properties the property object
 */
   _callback_overrideProperty: function(name, value, properties) {

      var D = Dx;

      var Class = this;

      var method_id, old;

      if (properties.hasOwnProperty(name)) {

         if (D.is('Function', value)) {

            method_id = D.Base.registerMethod(name, Class);

            if (method_id) {

               value._override_id = method_id;

               // copy super class call id
               old = Class[name];

               if ('_super_id' in old) {

                  value._super_id = old._super_id;

               }

            }

         }

         Class[name] = value;

      }

   }

});

